/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Singleton_h__
#define __dylab_Singleton_h__

//===========================================================================================
#include <dylab/utils/thread/GuardedObject.h>

//===========================================================================================
namespace dylab {

    /** Thread-safe singleton base class.
	@remarks
		To make a class singleton, simply derive it from this class.
	@note
		There are two subclasses for accessing the singleton instance:
		ScopedShLock for shared and ScopedExLock for exclusive access.
    */    
    template < 
        class _ClassT
    >
    class Singleton
    {
    private:

		static GuardedObject<_ClassT> s_mSingleton;

	public:
		/** Provides a shared access to the parent singleton.
		@remarks
			The access is limited by the scope of the ScopedShLock instance lifetime.
		*/
		class ScopedShLock : public GuardedObject<_ClassT>::ScopedShLock
		{
		public:
			ScopedShLock(const GuardedObject<_ClassT> & _guardedSingleton) 
				: GuardedObject<_ClassT>::ScopedShLock(&_guardedSingleton)
					{ }
		};
		//-----------------------------------------------------------------------------

	public:
		/** Provides an exclusive access to the parent singleton.
		@remarks
			The access is limited by the scope of the ScopedExLock instace lifetime.
		*/
		class ScopedExLock : public GuardedObject<_ClassT>::ScopedExLock
		{
		public:
			ScopedExLock(GuardedObject<_ClassT> & _guardedSingleton) 
				: GuardedObject<_ClassT>::ScopedExLock(&_guardedSingleton)
					{ }
		};
		//-----------------------------------------------------------------------------

    public:

        /** Store the singleton instance.
        @remarks
			Since the singleton class can have only one instance,
			the second call to this constructor will fail.
			After the destruction of this object
			the singleton can be created again.			
        */
        Singleton()
                { 
					DYLAB_ASSERT(!s_mSingleton.isValid());
					s_mSingleton = static_cast<_ClassT*>(this);
                }

        /** The sigleton destructor. */
		virtual ~Singleton()
                { s_mSingleton.invalidate(); }

	
        /** Returns the singleton object.
		@returns
			Returns the reference to the singleton instace encapsulated
			in the instance of class GuardedObject.
        */
        static GuardedObject<_ClassT> & getSingleton()
                { return s_mSingleton; }
    };
    
	template <class _ClassT> GuardedObject<_ClassT> Singleton<_ClassT>::s_mSingleton;
}

//===========================================================================================
// helper macro for retrieving singleton scoped shared lock
#define DYLAB_SINGLETON_SCOPED_SH_LOCK(_SingletonClass)\
	dylab::_SingletonClass::ScopedShLock(dylab::_SingletonClass::getSingleton())

// helper macro for retrieving singleton scoped exclusive lock
#define DYLAB_SINGLETON_SCOPED_EX_LOCK(_SingletonClass)\
	dylab::_SingletonClass::ScopedExLock(dylab::_SingletonClass::getSingleton())
//===========================================================================================
#endif // __dylab_Singleton_h__
